// Copyright 2025, Pulumi Corporation.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package display

import (
	"fmt"
	"io"
	"os"
	"regexp"
	"strings"

	"github.com/pulumi/pulumi/sdk/v3/go/common/diag/colors"
	"github.com/pulumi/pulumi/sdk/v3/go/common/util/cmdutil"
)

// NeoErrorSummaryMetadata contains metadata about a Neo error summary.
type NeoErrorSummaryMetadata struct {
	Summary string // The summary generated by Neo.
}

// ExplainFailureLink returns the link that will open Neo and trigger it to explain the failure on the given
// permalink.
func ExplainFailureLink(permalink string) string {
	return permalink + "?explainFailure"
}

// neoDelimiterEmoji provides an end of line delimiter for Neo diagnostics header.
// Either an emoji or a colon is displayed, rendering a colon next to an emoji looks cluttered.
// Exposed here for testing.
func neoDelimiterEmoji() string {
	return cmdutil.EmojiOr(" ✨", ":")
}

// RenderNeoErrorSummary renders a Neo error summary to the console.
func RenderNeoErrorSummary(summary *NeoErrorSummaryMetadata, err error, opts Options, permalink string) {
	out := opts.Stdout
	if out == nil {
		out = os.Stdout
	}

	// Neo does not generate a summary under some conditions. It may have been disabled on the server etc.
	// Don't render anything in that case.
	if err == nil && summary == nil {
		return
	}

	// Generate the header
	header := opts.Color.Colorize(
		colors.SpecHeadline + "Neo Diagnostics" + neoDelimiterEmoji() + colors.Reset)
	fmt.Fprintln(out, header)

	// Print the error if there was one and return.
	if err != nil {
		fmt.Fprintf(out, "  error summarizing update output: %s\n", err)
		fmt.Fprintln(out)
		return
	}

	summaryLines := strings.Split(summary.Summary, "\n")
	for _, line := range summaryLines {
		fmt.Fprintln(out, "  "+line)
	}

	fmt.Fprintln(out)

	PrintNeoLink(out, opts, permalink)
}

func PrintNeoLink(out io.Writer, opts Options, permalink string) {
	fmt.Fprintln(out, "  "+"Would you like additional help with this update?")
	fmt.Fprintln(out, "  "+
		opts.Color.Colorize(colors.Underline+colors.BrightBlue+ExplainFailureLink(permalink)+colors.Reset))
	fmt.Fprintln(out)
}

// RenderNeoThinking displays a "Thinking..." message.
func RenderNeoThinking(opts Options) {
	stdout := opts.Stdout
	if stdout == nil {
		stdout = os.Stdout
	}

	fmt.Fprintln(stdout, "Thinking...")
}

// FormatNeoSummary formats a Neo summary for display.
func FormatNeoSummary(summary string, opts Options) string {
	summary = renderBoldMarkdown(summary, opts)
	return fmt.Sprintf("\n%s\n", opts.Color.Colorize(summary))
}

// renderBoldMarkdown renders bold double-asterisk markdown in a summary as a colorized string.
// Double-asterisk markdown is very common in Neo summaries and is quite hard to read.
func renderBoldMarkdown(summary string, opts Options) string {
	summary = regexp.MustCompile(`\*\*(.*?)\*\*`).ReplaceAllString(summary, colors.BrightBlue+"$1"+colors.Reset)
	return summary
}
